package com.kingyea.mobilepolice.config.auth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;

/**
 * OAuth2服务配置
 * 管理access tokens的认证服务
 */

@Configuration
@EnableResourceServer
@EnableAuthorizationServer  //提供/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;// 为了使用“password”授权方式
    @Autowired
    private RedisConnectionFactory connectionFactory;// redis链接工厂
    @Autowired
    private UserDetailsService userDetailsService;// 获取用户服务

    /**
     * @author Mr.Lin
     * @date 2018/4/2 12:23
     */
    @Autowired
//    private ComboPooledDataSource dataSource;
//    private DruidDataSource dataSource;
    private DataSource dataSource;


    /**
     * 授权记录
     *
     * @author Mr.Lin
     * @date 2018/4/2 12:23
     */
    @Bean
    public ApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }

    /**
     * 授权码
     *
     * @author Mr.Lin
     * @date 2018/4/2 12:23
     */
    @Bean
    protected AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    /**
     * 使用redis来缓存token
     */
    @Bean
    public RedisTokenStore tokenStore() {
        RedisTokenStore redisTokenStore = new RedisTokenStore(connectionFactory);
//        redisTokenStore.setAuthenticationKeyGenerator(new DefaultAuthenticationKeyGenerator(){
//            protected String generateKey(Map<String, String> values) {
//                System.out.println(JavaJwtTokenUtils.createJavaJwtToken(Maps.newHashMap(values)));
//                return JavaJwtTokenUtils.createJavaJwtToken(Maps.newHashMap(values));
//            }
//        });
//        redisTokenStore.setSerializationStrategy(new JdkSerializationStrategy(){
//            protected <T> T deserializeInternal(byte[] bytes, Class<T> clazz) {
//                System.out.println(new String(bytes));
//                return super.deserializeInternal(bytes, clazz);
//            }
//        });
        return redisTokenStore;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)//若无，refresh_token会有UserDetailsService is required错误
//                .tokenServices(tokenServices()) //访问令牌&更新令牌 oauth_access_token & oauth_refresh_token  表 redis
                .tokenStore(tokenStore())
                .authorizationCodeServices(authorizationCodeServices()) //授权码 oauth_code 表
                .approvalStore(approvalStore()) //授权记录 oauth_approvals  表

        // 实现自定义页面 控制层 写 @SessionAttributes("authorizationRequest") 就不需要在这里配了，url对应默认的一样就行
//                .pathMapping("/login", "/extenal/login")
//                .pathMapping("/oauth/confirm_access", "/extenal/oauth/confirm_access")
//                .pathMapping("/oauth/authorize", "/extenal/oauth/authorize")
        ;


    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
                .tokenKeyAccess("permitAll()")
//                .checkTokenAccess("isAuthenticated()");
                .checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.jdbc(dataSource);// 客户端信息 oauth_client_details 表

        // "password","authorization_code","refresh_token","client_credentials","implicit"
//        clients.inMemory()
//                // 测试终端
//                .withClient("test")// withClient 和 secret 设置的就是 Basic Auth认证 信息
//                .secret("test")
//                .scopes("xx")//此处的scopes是无用的，可以随意设置
//                .authorizedGrantTypes("refresh_token", "password", "authorization_code", "implicit", "client_credentials")
//                // zuul_server
//                .and()
//                .withClient("zuul_server")// withClient 和 secret 设置的就是 Basic Auth认证 信息
//                .secret("zuul_server")
//                .scopes("*")//此处的scopes是无用的，可以随意设置
//                .authorizedGrantTypes("refresh_token", "password", "authorization_code", "implicit", "client_credentials")
//                // 内部微服务
//                .and()
//                .withClient("system_server")
//                .secret("system_server")
//                .scopes("read")// 在controller写@PreAuthorize("#oauth2.hasScope('read')")
//                .authorizedGrantTypes("client_credentials")
//                // web终端
//                .and()
//                .withClient("web")// withClient 和 secret 设置的就是 Basic Auth认证 信息
//                .secret("web")
//                .scopes("xx")//此处的scopes是无用的，可以随意设置
//                .authorizedGrantTypes("refresh_token", "password", "authorization_code", "implicit")
//        ;
    }


}

